package com.dylan.历史.java8.并发增强;

import com.dylan.历史.util.SleepUtils;
import org.junit.Test;

import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
import java.util.stream.Stream;

/**
 * Created by Dylan on 2018/7/30.
 */
public class 原子值 {

    /**
     * 当进行更新值时,为了保证原子操作,使用循环判断+compareAndSet
     */
    @Test
    public void test1() {
        AtomicLong largest = new AtomicLong();
        long newValue = 2342;
        // 循环判断,更新最新值
        long oldValue;
        do {
            oldValue = largest.get();
            newValue = Math.max(oldValue, newValue);
        } while (!largest.compareAndSet(oldValue, newValue));
    }

    /**
     * 当进行更新值时,为了保证原子操作,使用lambda表达式
     */
    @Test
    public void test2() {
        AtomicLong largest = new AtomicLong();
        long newValue = 2342;
        largest.updateAndGet(x -> Math.max(x, newValue));
    }

    /**
     * 当进行更新值时,为了保证原子操作,使用lambda表达式
     * accumulateAndGet() 可以指定累计方法
     */
    @Test
    public void test3() {
        AtomicLong largest = new AtomicLong(2);
        long newValue = 2342;
        largest.accumulateAndGet(newValue, (ov, nv) -> ov * 2 + nv);
    }

    /**
     * 如果存在高度竞争,使用LongAdder代替AtomicLong,提高效率
     */
    @Test
    public void test4() {
        LongAdder adder = new LongAdder();
        Stream.iterate(0, i -> i + 1).limit(10).forEach(i -> {
            new Thread(() -> {
                adder.increment();
                adder.add(2);
            }, "thread-" + i).start();
        });
        SleepUtils.seconds(2);
        System.out.println(adder.sum()); // AtomicLong内部由多个变量组成,所以用sum()来获取结果
    }

    /**
     * 通过指定算法和初始值进行累计
     */
    @Test
    public void test5() {
        LongAccumulator adder = new LongAccumulator(((left, right) -> left * 2 + right), 0);
        Stream.iterate(0, i -> i + 1).limit(10).forEach(i -> {
            new Thread(() -> {
                adder.accumulate(1);
            }, "thread-" + i).start();
        });
        SleepUtils.seconds(2);
        System.out.println(adder.get()); // 用get()获取最后的结果
    }
}
